/*
 * @Author: your name
 * @Date: 2021-10-12 00:29:25
 * @LastEditTime: 2021-10-12 13:52:13
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /Desktop/test.c
 */


#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h> 
#include <sys/wait.h>

#define MAX_LINE 80 /* The maximum length command */
char command[MAX_LINE]; // the command that was entered
// Redirect
int IO_index = 0;
int has_out = 0; // '>'
int has_in = 0; // '<'
char *fileName;


void do_redirect()
{   
    /* Redirection */
    int fd;
    int file_id;
    if (has_out) {
        // Open file - store output
        if ((file_id = open(fileName, O_CREAT|O_WRONLY, 0666)) == -1) {
            printf("open error\n");
            exit(-1);
        }
        fd = dup2(file_id, STDOUT_FILENO); // output will go to file
        if (fd == -1) { // catch error
            printf("dup2 error\n");
            exit(-1);
        }
    } else if (has_in) {
        // Open file - as input
        if ((file_id = open(fileName, O_RDONLY)) == -1) {
            printf("open error\n");
            exit(-1);
        }
        fd = dup2(file_id, STDIN_FILENO); // file content will be input
        if (fd == -1) { // catch error
            printf("dup2 error\n");
            exit(-1);
        }
    }
}


/* Copy the pointer list */
void storePointerList(char *A[], char *B[]) {
    int i = 0;
    while (B[i] != NULL) {
        char *pointer = B[i];
        char *history = (char*)malloc(sizeof(char)*MAX_LINE);
        for (int j = 0; j < MAX_LINE; j++) {
            history[j] = *pointer;
            pointer++;
        }
        A[i] = history;
        i++;
    }
    A[i] = NULL;
}

/* print Srgs looply*/
void showSrgs(char *A[]) {
    int i = 0;
    while (A[i] != NULL) {
        printf("%s", A[i]);
        i++;
    }
    printf("\n");
}

void do_history(char *args[]) {
    char *history_args[MAX_LINE/2 + 1]; // Store history cmd
    history_args[0] = NULL;

    if (!strcmp(args[0], "!!")) { // Done at Parent
        if (history_args[0] == NULL) {
            printf("No commands in history buffer\n");
            // continue;
        } 
        if (history_args[0] != NULL) {
            storePointerList(args, history_args); // load history
            // Show history cmd
            printf("History CMD: ");
            showSrgs(args);
        }
    } else {
        storePointerList(history_args, args); // Store the history
    }
}

int do_pip(char *args[], char *args2[], int pipe_index, int has_pipe) {
    // char *args2[MAX_LINE/2 + 1]; // Used to store the second command
    // args2[0] = NULL;
    // int pipe_index = 0;
    // int has_pipe = 0;
    // Check whether there is a pipe
    while (args[pipe_index] != NULL) {
        if (!strcmp(args[pipe_index], "|")) {
            has_pipe = 1;
            break;
        }
        pipe_index++;
    }
    // If has a pipe, store it (second one)
    if (has_pipe) {
        int args1_index = pipe_index + 1; // raw line
        int args2_index = 0; // new line
        while (args[args1_index] != NULL) {
            args2[args2_index++] = args[args1_index++];
        }
        args[pipe_index] = NULL; // args should stop here
        args2[args2_index] = NULL; // args2 should stop here
    }
    return has_pipe;
}

int action_pipe(int has_pipe, char *args[], char *args2[]) {
    if (has_pipe) {
        /* Create a child-child process do the args first
        then child process do the args2 */
        int fd2;
        int cpid; // child-child process id
        int cstate; // child-child process state
        int tempFile_id; // Used to store temporary message
        cpid = fork(); // child create child-child
        if (cpid < 0) {
            printf("Child: fork failed\n");
            return 1;
        }
        // Here is Child-Child
        else if (cpid == 0) {
            if ((tempFile_id = open("/tmp/1.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) {
                printf("open error\n");
                exit(-1);
            }
            fd2 = dup2(tempFile_id, STDOUT_FILENO); // let temp file to store output
            execvp(args[0],args); // execute the first cmd
            exit(0);
        }
        // Here is the previous child (fork caller)
        else {
            waitpid(cpid, &cstate, 0);
            close(fd2);
            if ((fd2 = open("/tmp/1.txt", O_RDONLY)) == -1) {
                printf("open error\n");
                exit(-1);
            }
            dup2(fd2, STDIN_FILENO); // let temp file be the input
            execvp(args2[0], args2); // execute the second one
            if (remove("/tmp/1.txt") < 0) {
                printf("remove error\n");
                exit(-1);
            }
            exit(0);
        }
    }
    // Not pipe
    else {
        execvp(args[0], args); // execute it
        printf("Child: exec failed, die\n");
        return 1; // This "return" is for Child
    }
}


/* MAIN */
int main(void)
{
    /* Setting arguments:
    command line (of 80) has max of 40 arguments */
    char *args[MAX_LINE/2 + 1]; // Store current cmd
    args[0] = NULL; // command
    char *history_args[MAX_LINE/2 + 1]; // Store history cmd
    history_args[0] = NULL;

    /* flag to determine when to exit program */
    int should_run = 1;

    while (should_run) {
        printf("$ ");  /* prompt */
        fflush(stdout);

        /* read a command from the keyboard */
        read(0, command, MAX_LINE);

        /* parse the command */
        int cmd_index = 1;
        args[0] = command; // store the first word's pointer
        for (int i = 0; i < MAX_LINE; i++) {
            // end of word
            if (command[i] == ' ') {
                command[i] = '\0';
                args[cmd_index] = &command[i+1];
                cmd_index++;
            }
            // the last word
            else if (command[i] == '\n') {
                command[i] = '\0';
                args[cmd_index] = NULL;
                break;
            }
        }

        /* Quit */
        if (!strcmp(args[0], "quit")) {
            printf("Programme quit\n");
            should_run = 0;
            break;
        }
        
        /* Check &, whether need to go background */
        int need_wait = 1;
        if (!strcmp(args[cmd_index-1], "&")) {
            need_wait = 0;
            args[cmd_index-1] = NULL; // Remove the '&'
        }

        /* Check whether to read history */
        // do_history(args);
        if (!strcmp(args[0], "!!")) { // Done at Parent
            if (history_args[0] == NULL) {
                printf("No commands in history buffer\n");
                continue;
            } else {
                storePointerList(args, history_args); // load history
                // Show history cmd
                printf("History CMD: ");
                showSrgs(args);
            }
        } else {
            storePointerList(history_args, args); // Store the history
        }

        /* Redirecting */
        // int IO_index = 0;
        // int has_out = 0; // '>'
        // int has_in = 0; // '<'
        while (args[IO_index] != NULL) {
            if (!strcmp(args[IO_index], ">")) {
                has_out = 1;
                break;
            } else if (!strcmp(args[IO_index], "<")) {
                has_in = 1;
                break;
            }
            IO_index++;
        }

        /* parse the command again */
        // char *fileName;
        if (has_in | has_out) {
            fileName = args[IO_index+1]; // the next of Orientation will be the last argument
            args[IO_index] = NULL; // args should stop here
        }

        /* Pipe */
        char *args2[MAX_LINE/2 + 1]; // Used to store the second command
        args2[0] = NULL;
        int pipe_index = 0;
        int has_pipe = 0;
        has_pipe = do_pip(args, args2, pipe_index, has_pipe);
        
        // // Check whether there is a pipe
        // while (args[pipe_index] != NULL) {
        //     if (!strcmp(args[pipe_index], "|")) {
        //         has_pipe = 1;
        //         break;
        //     }
        //     pipe_index++;
        // }
        // // If has a pipe, store it (second one)
        // if (has_pipe) {
        //     int args1_index = pipe_index + 1; // raw line
        //     int args2_index = 0; // new line
        //     while (args[args1_index] != NULL) {
        //         args2[args2_index++] = args[args1_index++];
        //     }
        //     args[pipe_index] = NULL; // args should stop here
        //     args2[args2_index] = NULL; // args2 should stop here
        // }

        /*
        * After reading user input, the steps are:
        * (1) fork a child process using fork()
        * (2) the child process will invoke execvp()
        * (3) parent will invoke wait() unless command included &
        */

        // Create child process
        pid_t pid; // child process id
        int status; // child process statues
        pid = fork(); // create child process now
        // If something error, child process can not be create
        if (pid < 0) {
            printf("Parent: fork failed\n");
            return 1; // This "return" is for Parent
        }
        else if(pid == 0) { 
			// printf("Child: now running redirection.");
            do_redirect();

            int res = action_pipe(has_pipe, args, args2);
            if (res == 1) {
                return res;
            }
			// printf("Child: now running the program, doing exec\n");
            
			// execvp(args[0], args);
			
			// if exec succeeded, the new child now starts executing the code of 
			// the main function of the xcalc program
			// The new child normally never executes the code below , unless exec failed.
			// printf("Child: exec failed, die\n");
			return 1;  // child process dies after failed exec.
		} else {
			// code only executed by the parent process
			// printf("Parent: Now sleeping and waiting for child %d to end\n", pid);
            if (need_wait) {
                wait(&status); // Waiting child process finished
                // printf("Parent: Finished waiting for child, child is dead\n");
                // printf("Parent: Child exit status is %d\n", status);
            }
			
		}
        // Here is child
        // else if (pid == 0) {
        //     /* Redirection */
        //     int fd;
        //     int file_id;
        //     if (has_out) {
        //         // Open file - store output
        //         if ((file_id = open(fileName, O_CREAT|O_WRONLY, 0666)) == -1) {
        //             printf("open error\n");
        //             exit(-1);
        //         }
        //         fd = dup2(file_id, STDOUT_FILENO); // output will go to file
        //         if (fd == -1) { // catch error
        //             printf("dup2 error\n");
        //             exit(-1);
        //         }
        //     } else if (has_in) {
        //         // Open file - as input
        //         if ((file_id = open(fileName, O_RDONLY)) == -1) {
        //             printf("open error\n");
        //             exit(-1);
        //         }
        //         fd = dup2(file_id, STDIN_FILENO); // file content will be input
        //         if (fd == -1) { // catch error
        //             printf("dup2 error\n");
        //             exit(-1);
        //         }
        //     }
        //     /* pipe */
            // if (has_pipe) {
            //     /* Create a child-child process do the args first
            //     then child process do the args2 */
            //     int fd2;
            //     int cpid; // child-child process id
            //     int cstate; // child-child process state
            //     int tempFile_id; // Used to store temporary message
            //     cpid = fork(); // child create child-child
            //     if (cpid < 0) {
            //         printf("Child: fork failed\n");
            //         return 1;
            //     }
            //     // Here is Child-Child
            //     else if (cpid == 0) {
            //         if ((tempFile_id = open("/tmp/1.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) {
            //             printf("open error\n");
            //             exit(-1);
            //         }
            //         fd2 = dup2(tempFile_id, STDOUT_FILENO); // let temp file to store output
            //         execvp(args[0],args); // execute the first cmd
            //         exit(0);
            //     }
            //     // Here is the previous child (fork caller)
            //     else {
            //         waitpid(cpid, &cstate, 0);
			// 		close(fd2);
            //         if ((fd2 = open("/tmp/1.txt", O_RDONLY)) == -1) {
            //             printf("open error\n");
            //             exit(-1);
            //         }
			// 		dup2(fd2, STDIN_FILENO); // let temp file be the input
			// 		execvp(args2[0], args2); // execute the second one
			// 		if (remove("/tmp/1.txt") < 0) {
            //             printf("remove error\n");
            //             exit(-1);
            //         }
			// 		exit(0);
            //     }
            // }
            // // Not pipe
            // else {
            //     execvp(args[0], args); // execute it
            //     printf("Child: exec failed, die\n");
            //     return 1; // This "return" is for Child
            // }
        // }
        // // Here is parent
        // else {
        //     if (need_wait) {
        //         wait(&status); // Waiting child process finished
        //     }
        //     // This place no longer need a "return", because the Parent need looping
        // }
        
    }
    return 0; 
}